package com.yueya.auth.realm;

import com.yueya.auth.config.AuthProperties;
import com.yueya.auth.config.MessageConfig;
import com.yueya.auth.model.Account;
import com.yueya.auth.service.AccountInfoProvider;
import com.yueya.auth.token.JwtToken;
import com.yueya.auth.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public abstract class PmsRealm extends AuthorizingRealm {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    private AccountInfoProvider provider;
    protected MessageConfig messageConfig;
    protected AuthProperties properties;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Principal principal = (Principal) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo info =  new SimpleAuthorizationInfo();
        Set<String> roles = new HashSet<>(principal.getRoles());
        Set<String> permissions = new HashSet<>(principal.getPermissions());
        if(!roles.isEmpty()){
            info.setRoles(roles);
        }
        if(!permissions.isEmpty()){
            permissions.forEach(r -> info.addStringPermission(r));
        }
        return info;
    }


    protected AuthenticationInfo getJwtAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        JwtToken token = (JwtToken)authenticationToken;
        if (token.getJwt() == null) {
            throw new AuthenticationException("no token");
        }
        Jws<Claims> jwt;
        try {
            jwt = JwtUtil.parseJwtPayload(token.getJwt(),properties.getPublicKey(),properties.getSignMode());
        } catch (JwtException e){
            logger.warn("验证异常",e);
            throw new AuthenticationException(e);
        }
        String id = jwt.getBody().get("userId",String.class);
        String loginName = jwt.getBody().get("loginName",String.class);
        String name = jwt.getBody().get("name",String.class);
        String photo = jwt.getBody().get("photo",String.class);
        // JavaType javaType = JsonMapper.getInstance().createCollectionType(Set.class,String.class);
        ArrayList<String> roles = jwt.getBody().get("roles",ArrayList.class);
        ArrayList<String> pres = jwt.getBody().get("pres",ArrayList.class);
        Account account = new Account();
        account.setId(id);
        account.setAccount(loginName);
        account.setName(name);
        account.setPhoto(photo);
        return new SimpleAuthenticationInfo(new Principal(account, roles,pres),token,getName());
    }

    protected AuthenticationInfo getFormAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        if(null==token.getPrincipal() || null==token.getCredentials()){
            throw new AuthenticationException(messageConfig.getMsgAccountPasswordEmpty());
        }
        String account = (String) token.getPrincipal();
        Account accountEntity = provider.loadAccount(account);
        if (null == accountEntity) {
            throw new AuthenticationException(messageConfig.getMsgAccountNotExist());
        }
        List<String> roles = new ArrayList<>(provider.loadRoles(accountEntity.getId()));
        List<String> permissions = new ArrayList<>(provider.loadPermissions(accountEntity.getId()));

        byte[] salt = Hex.decode(accountEntity.getPassword().substring(0,16));
        return new SimpleAuthenticationInfo(new Principal(accountEntity,roles,permissions),
                accountEntity.getPassword().substring(16),
                new ShiroByteSource(ByteSource.Util.bytes(salt)),
                getName());
    }
    public void setProvider(AccountInfoProvider provider) {
        this.provider = provider;
    }

    public void setMessageConfig(MessageConfig messageConfig) {
        this.messageConfig = messageConfig;
    }

    public void setProperties(AuthProperties properties) {
        this.properties = properties;
    }

}
